//! Modifier key data.
//!
//! Modifier keys like Shift and Control alter the character value
//! and are used in keyboard shortcuts.
//!
//! Use the constants to match for combinations of the modifier keys.

/// Pressed modifier keys.
///
/// Specification:
/// <https://www.w3.org/TR/uievents-key/#keys-modifier>
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Metas(u32);

impl Metas {
	/// Return `true` if a shift key is pressed.
	pub fn shift(&self) -> bool {
		self.contains(Metas::SHIFT)
	}
	/// Return `true` if a control key is pressed.
	pub fn ctrl(&self) -> bool {
		self.contains(Metas::CONTROL)
	}
	/// Return `true` if an alt key is pressed.
	pub fn alt(&self) -> bool {
		self.contains(Metas::ALT)
	}
	/// Return `true` if a meta key is pressed.
	pub fn meta(&self) -> bool {
		self.contains(Metas::META)
	}
}

impl Metas {
	pub const EMPTY: Self = Self::from_raw(0);
	pub const ALL: Self = Self::all();

	pub const ALT: Self = Self::from_raw(0x01);
	pub const ALT_GRAPH: Self = Self::from_raw(0x2);
	pub const CAPS_LOCK: Self = Self::from_raw(0x4);
	pub const CONTROL: Self = Self::from_raw(0x8);
	pub const FN: Self = Self::from_raw(0x10);
	pub const FN_LOCK: Self = Self::from_raw(0x20);
	pub const META: Self = Self::from_raw(0x40);
	pub const NUM_LOCK: Self = Self::from_raw(0x80);
	pub const SCROLL_LOCK: Self = Self::from_raw(0x100);
	pub const SHIFT: Self = Self::from_raw(0x200);
	pub const SYMBOL: Self = Self::from_raw(0x400);
	pub const SYMBOL_LOCK: Self = Self::from_raw(0x800);
	pub const HYPER: Self = Self::from_raw(0x1000);
	pub const SUPER: Self = Self::from_raw(0x2000);
}
impl Metas {
	#[inline]
	pub const fn empty() -> Self {
		Self(0)
	}
	#[inline]
	pub const fn from_raw(x: u32) -> Self {
		Self(x)
	}
	#[inline]
	pub const fn as_raw(self) -> u32 {
		self.0
	}
	#[inline]
	pub const fn is_empty(self) -> bool {
		self.0 == Self::empty().0
	}
	#[inline]
	pub const fn intersects(self, other: Self) -> bool {
		!Self(self.0 & other.0).is_empty()
	}
	#[inline]
	pub const fn contains(self, other: Self) -> bool {
		self.0 & other.0 == other.0
	}
}
impl ::std::ops::BitOr for Metas {
	type Output = Self;
	#[inline]
	fn bitor(self, rhs: Self) -> Self {
		Self(self.0 | rhs.0)
	}
}
impl ::std::ops::BitOrAssign for Metas {
	#[inline]
	fn bitor_assign(&mut self, rhs: Self) {
		*self = *self | rhs
	}
}
impl ::std::ops::BitAnd for Metas {
	type Output = Self;
	#[inline]
	fn bitand(self, rhs: Self) -> Self {
		Self(self.0 & rhs.0)
	}
}
impl ::std::ops::BitAndAssign for Metas {
	#[inline]
	fn bitand_assign(&mut self, rhs: Self) {
		*self = *self & rhs
	}
}
impl ::std::ops::BitXor for Metas {
	type Output = Self;
	#[inline]
	fn bitxor(self, rhs: Self) -> Self {
		Self(self.0 ^ rhs.0)
	}
}
impl ::std::ops::BitXorAssign for Metas {
	#[inline]
	fn bitxor_assign(&mut self, rhs: Self) {
		*self = *self ^ rhs
	}
}
impl ::std::ops::Not for Metas {
	type Output = Self;
	#[inline]
	fn not(self) -> Self {
		Self(!self.0)
	}
}
impl Metas {
	#[inline]
	pub const fn all() -> Self {
		Self::from_raw(
			Self::ALT.0
				| Self::ALT_GRAPH.0
				| Self::CAPS_LOCK.0
				| Self::CONTROL.0
				| Self::FN.0 | Self::FN_LOCK.0
				| Self::META.0
				| Self::NUM_LOCK.0
				| Self::SCROLL_LOCK.0
				| Self::SHIFT.0
				| Self::SYMBOL.0
				| Self::SYMBOL_LOCK.0
				| Self::HYPER.0
				| Self::SUPER.0,
		)
	}
}
impl std::fmt::Debug for Metas {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let mut f = f.debug_tuple("");
		if self.contains(Metas::ALT) {
			f.field(&"ALT");
		}
		if self.contains(Metas::ALT_GRAPH) {
			f.field(&"ALT_GRAPH");
		}
		if self.contains(Metas::CAPS_LOCK) {
			f.field(&"CAPS_LOCK");
		}
		if self.contains(Metas::CONTROL) {
			f.field(&"CONTROL");
		}
		if self.contains(Metas::FN) {
			f.field(&"FN");
		}
		if self.contains(Metas::FN_LOCK) {
			f.field(&"FN_LOCK");
		}
		if self.contains(Metas::META) {
			f.field(&"META");
		}
		if self.contains(Metas::NUM_LOCK) {
			f.field(&"NUM_LOCK");
		}
		if self.contains(Metas::SCROLL_LOCK) {
			f.field(&"SCROLL_LOCK");
		}
		if self.contains(Metas::SHIFT) {
			f.field(&"SHIFT");
		}
		if self.contains(Metas::SYMBOL) {
			f.field(&"SYMBOL");
		}
		if self.contains(Metas::SYMBOL_LOCK) {
			f.field(&"SYMBOL_LOCK");
		}
		if self.contains(Metas::HYPER) {
			f.field(&"HYPER");
		}
		if self.contains(Metas::SUPER) {
			f.field(&"SUPER");
		}
		f.finish()
	}
}
